#include <stdio.h>
#include <limits.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "aalib.h"
#include "aaint.h"
#include "aamktabl.h"
#include "config.h"
#define TABLESIZE 65536

#define pow2(i) ((i)*(i))
/*#define pow2(i) (abs(i)) */
#define add(i) if(next[(i)]==(i)&&last!=(i)) {if(last!=-1) next[last]=(i),last=(i); else last=first=(i);}
#define dist(i1,i2,i3,i4,i5,y1,y2,y3,y4,y5) (2*(pow2((int)(i1)-(int)(y1))+pow2((int)(i2)-(int)(y2))+(pow2((int)(i3)-(int)(y3))+pow2((int)(i4)-(int)(y4))))+1*pow2((int)(i5)-(int)(y5)))
#define dist1(i1,i2,i3,i4,i5,y1,y2,y3,y4,y5) ((pow2((int)(i1)-(int)(y1))+pow2((int)(i2)-(int)(y2))+(pow2((int)(i3)-(int)(y3))+pow2((int)(i4)-(int)(y4))))+2*pow2((int)(i5)-(int)(y5)))
#define pos(i1,i2,i3,i4) (((int)(i1)<<12)+((int)(i2)<<8)+((int)(i3)<<4)+(int)(i4))

#define postoparams(pos,i1,i2,i3,i4) \
  ((i1)=(pos)>>12),((i2)=((pos)>>8)&15),((i3)=((pos)>>4)&15),((i4)=((pos))&15)

static __AA_CONST int priority[]={4,5,3,2,1};

unsigned short *aa_mktable(aa_context * c)
{
	int i;
	int i1, i2, i3, i4;
	int sum, pos;
	__AA_CONST struct aa_font *currfont = c->params.font;
	int supported = c->params.supported;
	unsigned short *next;
	int first = -1;
	int last = -1;
	unsigned short *table;
	unsigned short *filltable;
	static struct parameters *parameters;
	next = (unsigned short *) malloc(sizeof(*next) * TABLESIZE);
	parameters = (struct parameters *) calloc(1, sizeof(struct parameters) * (NCHARS + 1));
	table = (unsigned short *) calloc(1, TABLESIZE * sizeof(*table));
	filltable = (unsigned short *) calloc(1, 256 * sizeof(*filltable));
	first = -1;
	last = -1;
	for (i = 0; i < TABLESIZE; i++)
		next[i] = i, table[i] = 0;
	__aa_calcparams(currfont, parameters, supported,c->params.dimmul, c->params.boldmul);

	for (i = 0; i < NCHARS; i++) {
		if (ALOWED(i, supported)) {
			int p1, p2, p3, p4;
			i1 = parameters[i].p[0];
			i2 = parameters[i].p[1];
			i3 = parameters[i].p[2];
			i4 = parameters[i].p[3];
			p1 = i1 >> 4;
			p2 = i2 >> 4;
			p3 = i3 >> 4;
			p4 = i4 >> 4;
			/*
			   if(p1>15) p1=15;
			   if(p2>15) p2=15;
			   if(p3>15) p3=15;
			   if(p4>15) p4=15; */
			sum = parameters[i].p[4];
			pos = pos(p1, p2, p3, p4);
			if (table[pos]) {
				int sum;
				p1 = (p1 << 4) + p1;
				p2 = (p2 << 4) + p2;
				p3 = (p3 << 4) + p3;
				p4 = (p4 << 4) + p4;
				sum = p1 + p2 + p3 + p4;
				if ((p1=dist(parameters[i].p[0],
						 parameters[i].p[1],
						 parameters[i].p[2],
						 parameters[i].p[3],
						 parameters[i].p[4],
						 p1, p2, p3, p4, sum)) >=
					(p1=dist(parameters[table[pos]].p[0],
						 parameters[table[pos]].p[1],
						 parameters[table[pos]].p[2],
						 parameters[table[pos]].p[3],
						 parameters[table[pos]].p[4],
						 p1, p2, p3, p4, sum))&&(p1!=p2||priority[i/256]<=priority[table[pos]/256]))
					goto skip;
			}
			table[pos] = i;
			add(pos);
		  skip:;

		}
	}
	for (pos = 0; pos < 256; pos++) {
		int mindist = INT_MAX, d1;
		for (i = 0; i < NCHARS; i++) {
			if (ALOWED(i, supported)) {
				if ((d1 = dist1(parameters[i].p[0],
								parameters[i].p[1],
								parameters[i].p[2],
								parameters[i].p[3],
								parameters[i].p[4],
								pos, pos, pos, pos, pos*4)) <= mindist&&(d1!=mindist||priority[i/256]>priority[filltable[pos]/256]))
					filltable[pos] = i, mindist = d1;
			}
		}
	}
	do {
		int blocked;
		if (last != -1)
			next[last] = last;
		else
			break;
		blocked = last;
		i = first;
		if (i == -1)
			break;
		first = last = -1;
		do {
			int m1, m2, m3, m4, ii, dm;
			unsigned short c = table[i];
			/* printf ("%c\n", c); */
			postoparams(i, m1, m2, m3, m4);
			for (dm = 0; dm < 4; dm++)
				for (ii = -1; ii <= 1; ii += 2) {
					int dist, dist1, index;
					unsigned short ch;
					i1 = m1;
					i2 = m2;
					i3 = m3;
					i4 = m4;
					switch (dm) {
					case 0:
						i1 += ii;
						if (i1 < 0 || i1 >= 16)
							continue;
						break;
					case 1:
						i2 += ii;
						if (i2 < 0 || i2 >= 16)
							continue;
						break;
					case 2:
						i3 += ii;
						if (i3 < 0 || i3 >= 16)
							continue;
						break;
					case 3:
						i4 += ii;
						if (i4 < 0 || i4 >= 16)
							continue;
						break;

					}
					index = pos(i1, i2, i3, i4);
					ch = table[index];
					if (ch == c || index == blocked)
						continue;
					if (ch) {
						int ii1 = (i1 << 4) + i1;
						int ii2 = (i2 << 4) + i2;
						int ii3 = (i3 << 4) + i3;
						int ii4 = (i4 << 4) + i4;
						int iisum = ii1 + ii2 + ii3 + ii4;
						dist = dist(
									   ii1, ii2, ii3, ii4, iisum,
									   parameters[c].p[0],
									   parameters[c].p[1],
									   parameters[c].p[2],
									   parameters[c].p[3],
									   parameters[c].p[4]);
						dist1 = dist(
										ii1, ii2, ii3, ii4, iisum,
										parameters[ch].p[0],
										parameters[ch].p[1],
										parameters[ch].p[2],
										parameters[ch].p[3],
										parameters[ch].p[4]);
					}
					if (!ch || dist < dist1) {
						table[index] = c;
						add(index);
					}
				}
			i1 = i;
			i = next[i];
			next[i1] = i1;
		}
		while (i != i1);
	}
	while (last != -1);
	c->table = table;
	c->filltable = filltable;
	c->parameters = parameters;
	free(next);
	return (table);
}
